package com.heima.search.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.ArticleSearchService;
import com.heima.utils.common.AppThreadLocalUtil;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ArticleSearchServiceImpl implements ArticleSearchService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private ApUserSearchService apUserSearchService;

    /*
     * Es根据关键字搜索
     * @param dto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto dto) throws IOException {
        //1.校验参数是否存在
        if (dto == null || StringUtils.isEmpty(dto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //保存用户的历史搜索记录(需要从Threadlocal中获得当前用户的id，所以需要添加一个线程存储用户id)
        if (dto.getFromIndex()==0 && AppThreadLocalUtil.getUser()!= null){
            apUserSearchService.insert(dto.getSearchWords(), AppThreadLocalUtil.getUser().getId());
        }

        //2.创建请求对象
        SearchRequest request = new SearchRequest("app_info_article");
        //3.准备DSL语句
        //3.1封装查询条件：按照关键字和发布时间查询，通过Bool查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //3.2通过关键字查询标题和文本内容，多字段查询
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery(dto.getSearchWords(),"title","content"));
        //3.3查询当前页面展示文章中发布的最小时间，用来分页
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime()));
        request.source().query(boolQueryBuilder);
        //3.4进行排序
        request.source().sort("publishTime", SortOrder.DESC);
        //3.5高亮
        //创建高亮对象，封装条件
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<font style='color:red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        request.source().highlighter(highlightBuilder);
        //3.6分页
        request.source().size(dto.getPageSize());
        //4.发送请求
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //5.解析数据
        //5.1获取数据
        SearchHits responseHits = response.getHits();
        SearchHit[] hits = responseHits.getHits();
        //5.2遍历数组中的数据
        List<Map> list = Arrays.stream(hits).map(searchHit -> {
            String json = searchHit.getSourceAsString();
            Map map = JSON.parseObject(json, Map.class);
            //5.3map集合中的title的数据没有高亮，需要解析高亮数据中的信息后替换掉
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            if (CollectionUtils.isNotEmpty(highlightFields)) {
                HighlightField title = highlightFields.get("title");
                if (title != null) {
                    //高亮数据中是一个数组，需要进行拼接成一个字符串,在添加到解析后的数据Map集合中
                    String h_title = StringUtils.join(title.getFragments(), ",");
                    map.put("h_title", h_title);
                } else {
                    //不存在高亮数据，则把原来的title数据添加到高亮字段中
                    map.put("h_title", map.get("title"));
                }
            }
            return map;
        }).collect(Collectors.toList());
        //6.返回结果
        return ResponseResult.okResult(list);
    }
}
